<?php
/**
 *	KLF Framework
 *
 *	Written by Kristian Oye <kristianoye@gmail.com> on Oct 29, 2011
 *	Copyright (C) 2010-2011
 */
namespace KLF\System
{
	use KLF\KCore;
	use KLF\System\Errors\KArgumentException;
	use KLF\System\Errors\KClassNotFoundException;
	
	/**
	 *  Class KType
	 *
	 *  A way to distinguish a class type from a simple string.
	 *
	 *  @package KLF\System
	 *  @access public
	 *  @author Kristian Oye <kristianoye@gmail.com>
	 *  @since 0.1
	 */
	final class KType
	{
		/**
		 * Contains a cache of all previously seen types.
		 * 
		 * @var array $_TypeCache
		 */
		private static $_TypeCache;
		
		/**
		 * Contains the name of the class or type.
		 * 
		 * @var string $_propTypeName
		 */
		private $_propTypeName; 
		
		private function __construct($type)
		{
			if (!is_string($type))
				throw new KArgumentException("Bad argument 1 to KType::__construct(); Must be a string.");
			
			$this->_propTypeName = $type;
		}
		
		/**
		* Returns the name of the actual type.
		 *
		* @return string
		*/
		public function GetName()
		{
			return $this->_propTypeName;
		}
		
		/**
		 * Create a KType object based on the type of the parameter.
		 * 
		 * @param mixed $arg
		 * @return \KLF\System\KType
		 */
		public static function GetType()
		{
			if (func_num_args() === 1) {
				$args = func_get_args();
				$arg = $args[0];
			}
			elseif (func_num_args() === 0) {
				$type = get_called_class();
			}
			if (!isset($arg) || is_null($arg)) {
				$type = 'NULL';
			}
			elseif (is_object($arg)) {
				$type = get_class($arg);
			}
			else 
			{
				$type = gettype($arg);
			}
			if(!isset(self::$_TypeCache))
			{
				self::$_TypeCache = array();
			}
			if(!isset(self::$_TypeCache[$type]))
			{
				self::$_TypeCache[$type] = new KType($type);
			}
			return self::$_TypeCache[$type];
		}
		
		/**
		 * Returns a KType object by looking up the qualified class name.
		 * 
		 * @param string $type
		 * @return KType
		 * @throws KClassNotFoundException
		 */
		public static function GetTypeByClass($type='')
		{
			if(!isset(self::$_TypeCache))
			{
				self::$_TypeCache = array();
			}
			if ($type === NULL)
			{
				return NULL;
			}
			elseif (is_object($type))
			{
				if (get_class($type) === get_called_class())
				{
					return $type->_propTypeName;
				}
			}
			elseif (!isset(self::$_TypeCache[$type]))
			{
				if (!class_exists($type))
				{
					switch(strtolower($type))
					{
						case 'int': case 'integer':
						case 'real': case 'double': case 'float':
						case 'string': case 'array':
						case 'bool': case 'boolean':
						case 'null':
							if (!isset(self::$_TypeCache[$type]))
							{
								self::$_TypeCache[$type] = new KType($type);
							}
							break;
						default: throw new KClassNotFoundException($type);
					}
				}
				else 
				{
					self::$_TypeCache[$type] = new KType($type);
				}
			}
			return self::$_TypeCache[$type];
		}
		
		/**
		 * Determine if the type passed is of the same type or a descendant.
		 * 
		 * @param KType $type
		 */
		public function IsType(KType $type)
		{
			
		}
		
		/**
		 * Create a new instance of the type contained within.
		 * 
		 * @return mixed
		 */
		public function NewInstance()
		{
			$args = func_get_args();
			switch($this->_propTypeName)
			{
				case 'array': return count($args) > 0 ? (array)$args : array();
				case 'boolean': return count($args) > 0 ? (boolean)$args[0] : false;
				case 'float': return count($args) > 0 ? (float)$args[0] : 0.0;
				case 'integer': return count($args) > 0 ? (integer)$args[0] : 0;
				case 'NULL': return NULL;
				case 'string': return count($args) > 0 ? (string)$args[0] : '';
				default:
					$class = new \ReflectionClass($this->_propTypeName);
					return $class->newInstanceArgs($args);
			}
		}
		
		/**
		 * Display the type contained within.
		 */
		public function __toString()
		{
			return get_class($this)."(".$this->_propTypeName.")";
		}
	}
}